import axios, { AxiosRequestConfig } from "axios";
// npm i qs
import qs from "qs";
import utils from "./utils";
import {
  infoNotification,
  warningNotification,
  errorNotification,
  convertToBoolean,
} from "./utils";
import bus from "./bus";
// import { useI18n } from "vue-i18n";
// const { t } = useI18n({ useScope: "global" });
// 在 setup 外应用
import i18n from "../i18n/index";
const { t } = i18n.global;
const env = import.meta.env;

// tsconfig.json 允许索引 "suppressImplicitAnyIndexErrors": true,
interface ICODE_MESSAGE {
  [key: number]: string;
}
const CODE_MESSAGE: ICODE_MESSAGE = {
  200: "服务器成功返回请求数据",
  201: "新建或修改数据成功",
  202: "一个请求已经进入后台排队(异步任务)",
  204: "删除数据成功",
  400: "发出信息有误",
  401: "用户没有权限(令牌失效、用户名、密码错误、登录过期)",
  402: "令牌过期",
  403: "用户得到授权，但是访问是被禁止的",
  404: "访问资源不存在",
  406: "请求格式不可得",
  410: "请求资源被永久删除，且不会被看到",
  500: "服务器发生错误",
  502: "网关错误",
  503: "服务不可用，服务器暂时过载或维护",
  504: "网关超时",
};

/**
 * @description 当会话超时或账号不存在（系统重新初始化数据）等情况，需要锁定全部请求。注销到登录页，登录页获取公钥、登录时恢复请求。 **/
let requestLocked = false;
let source = axios.CancelToken.source();

/**
 * @description 函数返回唯一的请求key **/
const getRequestKey = (config: AxiosRequestConfig) => {
  let { method, url, params, data } = config;
  // axios中取消请求及阻止重复请求的方法
  // 参数相同时阻止重复请求：
  // return [method, url, JSON.stringify(params), JSON.stringify(data)].join("&");
  // 请求方法相同，参数不同时阻止重复请求
  return [method, url].join("&");
};

/**
 * @description 实例化 axios
 */
const instance = axios.create({
  // mock 模式下，url 只能使用相对路径，如果 url 有完整的 http 协议部分则不会被 mock 拦截
  baseURL: convertToBoolean(<string>(<unknown>env.VITE_MOCK_ENABLED))
    ? ""
    : env.VITE_API_URL,
  // 最长请求时间
  timeout: 10000,
  // crossDomain: true,
  // 跨域是否在请求中协带 cookie
  withCredentials: true,
  headers: {
    // 配后端数据的接收方式application/json;charset=UTF-8 或 application/x-www-form-urlencoded;charset=UTF-8
    "Content-Type": convertToBoolean(<string>(<unknown>env.VITE_MOCK_ENABLED))
      ? "application/json;charset=UTF-8"
      : "application/x-www-form-urlencoded;charset=UTF-8",
    // "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
  },
});

// Add a request interceptor
instance.interceptors.request.use(
  function (config) {
    // Do something before request is sent
    // config.headers.Test = "special get headers";

    if (
      (config.url || "").indexOf("public.key.get") > -1 ||
      (config.url || "").indexOf("login.by.account") > -1
    ) {
      // 获取公钥、登录时恢复请求
      // console.log(
      //   "instance.interceptors.request 获取公钥、登录时恢复请求",
      //   config.url
      // );
      requestLocked = false;
    }

    //  A `CancelToken` is an object that can be used to request cancellation of an operation
    config.cancelToken = source.token;
    if (requestLocked) {
      // 当锁定时，取消后续提交的全部请求
      console.log("instance.interceptors.request requestLocked", config.url);
      // 取消请求会触发响应的异常 function (error) {}
      source.cancel();
    }

    // let key = getRequestKey(config);
    // config.headers["hashcode"] = key;

    // console.log(headers());
    let h = headers();
    for (let key in h) {
      config.headers[key] = h[key];
    }
    // let ver = store.getAppVer()
    // if(ver){
    // 	config.headers["lok-app-ver"] = ver;
    // }
    return config;
  },
  function (error) {
    utils.errorNotification(t("common.checkAxios"), null);
    console.log(
      "Something happened in setting up the request that triggered an Error: ",
      error.message
    );
    // return Promise.reject(error);
  }
);

// Add a response interceptor
instance.interceptors.response.use(
  function (response) {
    // Any status code that lie within the range of 2xx cause this function to trigger
    if (response.status != 200) {
      // 右上角统一显示错误信息
      // 不在定义中的返回 undefined
      let msg = CODE_MESSAGE[response.status];
      if (msg != undefined) {
        // store.getters.getLocale;
        // 网络请求异常
        utils.errorNotification(
          t("common.networkRequestException") + ":" + msg,
          msg
        );
      } else {
        utils.errorNotification(
          t("common.networkRequestException") + "...",
          null
        );
      }

      // console.error(
      //   "The server responded with a status code: ",
      //   response.status,
      //   msg
      // );
    } else if (response.status == 200) {
      // 第一个条件排除分页接口
      // debugger;
      if (response.data.code !== undefined && response.data.code != 200) {
        if (
          response.data.code == 500501 ||
          response.data.code == 500503 ||
          response.data.code == 500505 ||
          (response.data.code == 400 &&
            response.data.msg == t("login.accountNotExists"))
        ) {
          if (!requestLocked) {
            // 清理当前登录账号缓存信息
            window.store.clearSession();
            // token 失效，注销账号，您长时间未操作，登录凭证已过期，请重新登录，Header.vue 处理
            bus.emit("login-time-out", {
              msg: t("login.certificateExpired"),
            });
            // 清理会话，上面的消息会触发重新登录，重新登录时会重新获取有效 publicKey
            // window.store.removeSession("publicKeySessionId");
          }
          // 锁定请求
          requestLocked = true;
        } else if (response.data.msg != "") {
          errorNotification(response.data.msg, null);
        } else {
          errorNotification(CODE_MESSAGE[response.data.code], null);
        }

        // console.log(
        //   "The server responded with a business code:",
        //   response.config.url,
        //   response.data.code,
        //   response.data.msg
        // );
      }
    }
    // console.log("The server responded:", response.config.url, response);
    return response;
  },
  function (error) {
    if (requestLocked) {
      // 锁定的时候不抛出异常
      console.log("instance.interceptors.response requestLocked");
      return;
    } else {
      // Any status codes that falls outside the range of 2xx cause this function to trigger
      // 服务器未响应
      utils.errorNotification(t("common.serverNotResponding"), null);
      console.log(
        "The request was made but no response was received",
        error.message != undefined ? ": " + error.message : ""
      );
    }
    // return Promise.reject(error);
  }
);

// import { headers } from "../../../utils/request";
export function headers(): any {
  // console.log("headers run");
  let list = {};
  let tokenInfo = window.store.tokenInfo;
  // console.log("instance.interceptors.request", tokenInfo);
  if (tokenInfo) {
    list["x-summer-token"] = tokenInfo.token;
    // vab 集成
    // config.headers["Authorization"] = "Bearer " + tokenInfo.token;
  }
  list["x-summer-lang"] = window.store.appLocale;
  return list;
}

export function get(url: string, params: any) {
  //`params` are the URL parameters to be sent with the request
  let mock = convertToBoolean(<string>(<unknown>env.VITE_MOCK_ENABLED));
  return instance.get(url, { params: params });
}

export function post(url: string, data: any) {
  //`data` is the data to be sent as the request body
  // console.log("request post", qs.stringify(data));
  // mock 模式下，数据只能是 json 格式的，否则 mock 参数 options.body 接收不到数据
  let mock = convertToBoolean(<string>(<unknown>env.VITE_MOCK_ENABLED));
  if (mock) {
    return instance.request({ url, method: "post", data: data });
  } else {
    if (utils.isFormData(data)) {
      // FormData 对象，上传的附件
      // axios 会根据提交的数据，智能修改 Content-Type 为 multipart/form-data
      return instance.request({ url, method: "post", data: data });
    }
    return instance.request({ url, method: "post", data: qs.stringify(data) });
  }
}

// export function upload(url: string, formData: FormData) {
//   //`data` is the data to be sent as the request body
//   // console.log("request post", qs.stringify(data));
//   // mock 模式下，数据只能是 json 格式的，否则 mock 参数 options.body 接收不到数据
//   let mock = convertToBoolean(<string>(<unknown>env.VITE_MOCK_ENABLED));
//   // instance.head["Content-Type"] = "multipart/form-data";
//   // if (mock) {
//   //   return instance.request({ url, method: "post",   }, data, data);
//   // } else {
//   //   return instance.request({ url, method: "post", data: qs.stringify(data) });
//   // }

//   console.log("typeof data", utils.isFormData(formData));

export default instance;
